#pragma once

#include <string>
#include <set>
#if (defined(__clang__) && defined(_LIBCPP_VERSION)) || (__cplusplus >= 201103L)
#include <memory>
#else
#include <tr1/memory>
#endif
#include <stdio.h>
#include <time.h>
#include <pthread.h>
#include <sys/types.h>
#include <regex.h>
#include <curl/curl.h>
#include "BBS2chProxyThreadInfo.h"
#include "BBS2chProxyAuth.h"
#include "IBBS2chProxySocket.h"
#include "BBS2chProxyHttpHeaders.h"
#include "BBS2chProxyURL.h"
#include "BBS2chProxyKeyManager.h"
#include "BBS2chProxyBoardManager.h"
#include "BBS2chProxyHTML2Dat.h"
#include "BBS2chProxyFormData.h"

class BBS2chProxyConnection {
public:
	struct AcceptEncodingChecker {
		std::set<std::string> _allowedEncodings;
		AcceptEncodingChecker() {
			_allowedEncodings.insert("identity");
		};
		void registerEncodings(curl_version_info_data *info);
		bool isSupported(const std::string &header);
	};
  private:
	int sock_c;
	BBS2chProxyThreadCache *threadCache;
	BBS2chProxyURL requestURL;
	static BBS2chProxyBoardManager boardManager;
	
  public:
	int content_length;
	int status;
	bool isResponseChunked;
	bool force5ch;
	bool bbscgi;
	IBBS2chProxySocket *socketToClient;
	bool isClientChunked;
	bool isClientHttp1_0;
	CURL *curl;
	bool isHttps;
	int directDatDownloading;
	int serverPort;
	static BBS2chProxyAuth auth;
	static BBS2chProxyKeyManager keyManager;
	static AcceptEncodingChecker acceptEncodingChecker;
	static BBS2chProxyCookieAuthManager cookieAuthManager;
	
  public:
	BBS2chProxyConnection(int socket, int port, BBS2chProxyThreadCache *cache) :
		sock_c(socket), serverPort(port), threadCache(cache),
		content_length(0), status(0), isResponseChunked(false), force5ch(false), bbscgi(false),
		socketToClient(NULL), isClientChunked(false), isClientHttp1_0(false), curl(NULL), isHttps(false), directDatDownloading(0)
	{
	};
	~BBS2chProxyConnection()
	{
		if (socketToClient) delete socketToClient;
	};
	
	void run(void * (*func)(void *));
	void connect(void);
	void setMonaKey(const std::string &key, int reason);
	static void compileRegex(void);
	
  private:
	int datProxy(IBBS2chProxyHTML2Dat &html2dat, const char *method, BBS2chProxyHttpHeaders &requestHeaders);
	int datProxyAPI(const std::string &url, const char *method, BBS2chProxyHttpHeaders &requestHeaders);
	int bbsmenuProxy(const std::string &url, const char *method, BBS2chProxyHttpHeaders &requestHeaders);
	int bbsCgiProxy(BBS2chProxyURL &url, BBS2chProxyHttpHeaders &requestHeaders, BBS2chProxyFormData &requestBody, bool talkTo5ch);
	int subjectTxtProxy(BBS2chProxyURL &url, const char *method, BBS2chProxyHttpHeaders &requestHeaders);
	int tunnel(const char *addr, int port);
	BBS2chProxyURL getRawDatURLAndStatus(BBS2chThreadIdentifier &threadIdentifier, BBS2chProxyHttpHeaders &requestHeaders, bool findKakologOnly, long *status, bool *foundAsKakolog);
};

#if (defined(__clang__) && defined(_LIBCPP_VERSION)) || (__cplusplus >= 201103L)
typedef std::shared_ptr<BBS2chProxyConnection> PBBS2chProxyConnection;
#else
typedef std::tr1::shared_ptr<BBS2chProxyConnection> PBBS2chProxyConnection;
#endif
